Inhalt: Es werden einige der ISO-nderungen/Erweiterungen gegenber dem
        blichen (Wirth)Sprachstandard beschrieben. Diese Aufzhlung ist
        weder vollstndig noch exakt!
        Der Abschnitt ber Exceptions sollte vor Benutzung der ISO-Module
        gelesen werden.


o Numerische Typen

  Bei den ganzzahligen Typen gibt es nur noch CARDINAL und INTEGER.
  Dies sind die grten verfgbaren Ganzzahltypen; bei Bedarf knnen
  Unterbereichstypen deklariert werden, die der Compiler dann bzgl. des
  Speicherplatzbedarfs optimieren kann (aber nicht mu). Es knnen
  zustzlich auch Typen wie LONGCARD oder SHORTCARD bereitgestellt
  werden, das sind dann aber Erweiterungen des jeweiligen Compilers.
  Da es nur noch jeweils einen Ganzzahltyp gibt, entfallen auch
  Kennzeichnungen fr LONG-Konstanten, wie z.B. ein nachgestelltes L oder D.

  Fr CARDINAL und INTEGER gibt es zwei neue Operatoren: REM und /.
  Fr positive Argumente verhalten sich diese beiden Operatoren genauso
  wie MOD und DIV, Unterschiede gibt es jedoch bei negativen Argumenten.
  Um das ganze weiter zu komplizieren: REM und / liefern bei negativen
  Argumenten die Ergebnisse, die frher MOD und DIV geliefert haben,
  whrend diese jetzt umdefiniert wurden. Das Verhalten wird aus der
  folgenden Tabelle ersichtlich (aus dem Standard):

    ---------------------------------------------------------------
    | op  | 31 op 10 | 31 op (-10) | (-31) op 10 | (-31) op (-10) |
    +-----+----------+-------------+-------------+----------------+
    |  /  |    3     |    -3       |      -3     |       3        |
    | REM |    1     |     1       |      -1     |      -1        |
    | DIV |    3     |  Ausnahme   |      -4     |   Ausnahme     |
    | MOD |    1     |  Ausnahme   |       9     |   Ausnahme     |
    ---------------------------------------------------------------

  Bei den reellen Zahlen gibt es REAL und LONGREAL, wie es auch schon
  die meisten Modula-Systeme frher geboten haben. LONGREAL mu mindestens
  den Zahlenbereich von REAL umfassen, kann aber auch identisch (vom
  Zahlenbereich, nicht vom Typ) mit REAL sein. Die beiden Typen sind
  allerdings nicht (mehr) zuweisungskompatibel, sie mssen explizit
  z.B. von LONGREAL nach REAL mit FLOAT und umgekehrt mit LFLOAT (neue
  Funktion) umgewandelt werden. Fr LONGREAL-Konstanten gibt es keine
  besondere Kennzeichnung, wie z.B. ein D als Exponent. Konstanten
  passen sich in der Genauigkeit dem umgebenden Ausdruck an, soweit das
  mglich ist.

  Als neuen Zahlentyp gibt es komplexe Zahlen: COMPLEX und LONGCOMPLEX.
  Komplexe Zahlen sind aus einem Realteil und einem Imaginrteil vom
  Typ REAL bzw. LONGREAL zusammengsetzt. Fr den Zahlenbereich von
  LONGCOMPLEX im Verhltnis zu COMPLEX gilt das gleiche wie fr LONGREAL
  und REAL. Als vordefinierte Operationen gibt es die vier Grundrechenarten
  und den Test auf (Un)Gleichheit (+, -, *, /, =, <>, #), ein Vergleich
  auf kleiner/grer existiert nicht. Aus (LONG)REAL-Variablen oder
  Konstanten knnen mit dem Konstruktor CMPLX komplexe Zahlen gebildet
  werden:

    c := CMPLX(1.0, 2.0) + c;
    c := CMPLX(x, y);

  Mit den Selektoren RE und IM kann der Real- bzw. Imaginrteil (vom Typ
  (LONG)REAL) einer komplexen Zahl ermittelt werden:

    r := RE(c) * 5.0;
    r := IM(c) - 1.0;


o RECORDs, ARRAYs

  Zur Laufzeit lassen sich jetzt Ausdrcke von RECORD- und ARRAY-Typen
  (value constructors, Aggregate) erstellen. Bei den einzelnen Elementen ist
  man nicht auf Konstanten festgelegt, sondern kann auch Variablen verwenden.
  Die Syntax ist der der Mengen angeglichen, genauer gesagt sind Ausdrcke
  von Mengen auch Value Constructors. Dabei werden die einzelnen Elemente
  durch Kommata getrennt und in geschweifte Klammern eingeschlossen; vor
  der ffnenden Klammer steht der Typ des Ausdrucks. Sind einzelne Elemente
  selber strukturiert, mu deren Typ nicht angegeben werden. Bei varianten
  RECORDs mu der Selektor als zustzliche Konstante aufgenommen werden,
  auf die dann die zum Selektor passenden RECORD-Elemente folgen. Bei ARRAYs
  kann ein Wiederholungsfaktor angegeben werden, wenn Feldelemente mit
  demselben Wert belegt werden sollen.

  Beispiele (aus dem Standard):

    TYPE RowType   = ARRAY [1..3] OF REAL;
         ArrayType = ARRAY [1..2] OF RowType;

    VAR  row   : RowType;
         array : ArrayType;

    ...

    row := RowType{1.0, 1.0, 1.0};
    row := RowType{1.0 BY 3};

    array := ArrayType{row, row};
    array := ArrayType{row BY 2};
    array := ArrayType{RowType{1.0, 2.0, 3.0}, RowType{1.0, 2.0, 3.0}};
    array := ArrayType{RowType{1.0, 2.0, 3.0} BY 2};
    array := ArrayType{{1.0, 2.0, 3.0}, row};


    TYPE NameType = ARRAY [0..24] OF CHAR;
         DateType = RECORD
           year, month, day : CARDINAL;
         END;
         PersonType = RECORD
           name  : NameType;
           birth : DateType;
         END;

    VAR  day, month, year : CARDINAL;
         date             : DateType;
         person, nobody   : PersonType

    ...

    date   := DateType{year, month, day}
    date   := DateType{1623, 6, 19}
    person := PersonType{"Blaise Pascal", date}
    person := PersonType{"Blaise Pascal", DateType{1623, 6, 19}}
    nobody := PersonType{NameType{"" BY 25}, DateType{0, 0, 0}}
    nobody := PersonType{{"" BY 25}, {0, 0, 0}}


o Konvertierung

  Zu unterscheiden ist zwischen der Typ-Uminterpretation (Typtransfer) und
  der Typ-Konvertierung. Bei der ersten Form wird der eigentliche Wert, das
  Bitmuster, des Ausdrucks nicht gendert, whrend bei der zweiten Form
  tatschlich Typen ineinander umgewandelt werden, wofr gegebenenfalls auch
  Code erzeugt wird. Frher wurden andere Methoden verwendet, um diese
  Effekte zu erreichen, wobei jeder Hersteller seine eigene Meinung von der
  Aufgabenteilung hatte; auerdem waren die Mglichkeiten meistens sehr
  beschrnkt. Fr die Uminterpretation wurde zumeist die Syntax mit dem Typ
  als Funktionsname verwendet (z.B. INTEGER(<expr)), whrend fr die
  Konvertierung die Funktion VAL zustndig war. Manchmal war aber auch nur
  eine der beiden Mglichkeiten vorhanden.

  Bei ISO ist fr die Uminterpretation die neue Funktion SYSTEM.CAST
  zustndig, wobei die Syntax der von VAL entspricht:

    intval := CAST(INTEGER,cardval);

  Haben Quell- und Zieltyp den gleichen Speicherplatzbedarf, bleibt das
  Bitmuster des Ausdrucks vollstndig erhalten, sonst bleibt es auf jeden
  Fall bis zur kleineren der beiden Gren erhalten, whrend der Rest
  implementierungsspezifisch ist. Die alte Syntax mit dem Typnamen als
  Funktion ist nicht mehr erlaubt.

  Fr die Konvertierung ist die alte Funktion VAL zustndig, die jedoch
  sehr viel universeller geworden ist. So akzeptiert sie jetzt jede
  sinnvolle Kombination von Typen; z.B. ist auch die Konvertierung von
  und nach (LONG)REAL mglich. Damit sind z.B. folgende Konvertierungen
  identisch:

    r := VAL(REAL,card);
    r := FLOAT(card),

  Fr die Konvertierung nach LONGREAL gibt es die neue Funktion LFLOAT.
  FLOAT und LFLOAT akzeptieren auch (LONG)REAL-Typen, so da eine einfache
  Konvertierung zwischen LONGREAL und REAL mglich ist:

    lr := LFLOAT(r) + lr;
    r  := FLOAT(lr) + r;

  Eine weitere neue Konvertierungsfunktion ist INT, die eine Ergnzung zu
  ORD fr INTEGER-Resultate ist. INT akzeptiert im Gegensatz zu ORD auch
  (LONG)REAL-Ausdrcke als Parameter. Fr die Konvertierung von positiven
  (LONG)REAL-Ausdrcken nach CARDINAL ist weiterhin TRUNC zustndig.


o Mengen

  Bei Wirth gab es nur einen Typ von Mengen, bei dem stillschweigend eine
  Abbildung der Mengenelemente auf Bits angenommen wurde. Nun gibt es
  zwei verschiedene Typen:

  * Die mit den neuen Schlsselwort PACKEDSET deklarierten Mengen, z.B.:

    TYPE Bitmenge = PACKEDSET OF [0..15];

    werden explizit mit Bitmengen assoziiert, fr die die folgenden
    Punkte gelten:

    - Wenn 'n' Element der Menge ist, dann ist das Bit 'n' gesetzt,
      sonst ist es gelscht.

    - Bit 0 ist das niederwertigste Bit, und aufeinanderfolgende
      Bitnummern reprsentieren aufeinanderfolgende Bits.

    - Neben den blichen Mengenoperationen gibt es noch die neuen
      Funktionen SYSTEM.SHIFT und SYSTEM.ROTATE, mit denen diese
      Mengen um Bitpositionen verschoben und (logisch) rotiert werden
      knnen. Bei einigen Compilern gab es diese beiden Funktionen
      schon vorher, sie waren aber auch fr andere Typen als (Bit)Mengen
      geeignet.

    - BITSET ist ein vordefinierter PACKEDSET-Typ.

    Falls BITSET und CARDINAL dieselbe Speichergre haben, gilt damit
    z.B. folgendes:

      CAST(CARDINAL,BITSET{0}) = VAL(CARDINAL,1)

      SHIFT(CAST(BITSET,card), 1) = card * 2
      SHIFT(CAST(BITSET,card), -1) = card DIV 2

  * bei den mit SET deklarierten Mengen ist die interne Reprsentation
    nicht festgelegt, es mu insbesondere keine Abbildung auf Bits geben.

    - Die Implementierung mu mindestens die Deklaration von SET OF CHAR
      erlauben.

  Fr die Bildung von Ausdrcken beider Mengentypen gilt:

    - Der Basistyp mu immer angegeben werden, es mu also z.B. heien:
      BITSET{0..3}, und nicht: {0..3}.

    - Als einzelne Mengenelemente oder Bereiche sind nun auch Variablen
      zugelassen:

      set := Menge{0,i,5,k..j};



o Adrearithmetik

  Die Typen ADDRESS und CARDINAL sind nicht mehr (ausdrucks)kompatibel,
  stattdessen mssen jetzt neue Funktionen aus SYSTEM benutzt werden, um
  mit Adressen zu rechnen: ADDADR und SUBADR ersetzen die Addition und
  Subtraktion mit beliebigen Pointer-Ausdrcken. Dabei wird der angegebene
  CARDINAL-Wert addiert bzw. subtrahiert, die Elementgre des Pointers
  wird nicht bercksichtigt. Mit DIFADR lt sich die Differenz zweier
  Pointer berechnen (z.B. fr einen Vergleich) und mit MAKEADR wird ein
  ADDRESS-Wert aus dem/n Parameter(n) erzeugt.

  Beispiele:

  adr1 := ADDADR(adr1, 10);
  adr1 := SUBADR(ADDADR(adr1, 5), 20);
  IF DIFADR(adr1, adr2) < 0 THEN
    (* adr1 < adr2 *)

  adr1 := MAKEADR(1000);



o Modulterminierung

  Bislang gab es nur eine Modulinitialisierung beim Programmstart (alles,
  was zwischen dem Modul-BEGIN und dem Modul-END steht, wird beim
  Programmstart automatisch ausgefhrt). Jetzt gibt es auch eine
  Modulterminierung, die bei Programmende ausgefhrt wird. Zustndig hierfr
  ist das neue Schlsselwort FINALLY. Alles, was zwischen dem Modul-BEGIN
  und dem FINALLY steht, gehrt zur Initialisierung, und wird wie bisher
  bei Programmstart ausgefhrt. Alles, was zwischen dem FINALLY und dem
  Modul-END steht, gehrt zur Terminierung. Die Terminierungen werden in
  der umgekehrten Reihenfolge wie die Initialisierungen ausgefhrt, so da
  sich eine Schachtelung ergibt. Sowohl Modulinitialisierung als auch
  Modulterminierung sind allerdings optional.


o Ausnahmen (Exceptions)

  Dies ist wohl die wichtigste und auch tiefgreifendste Erweiterung. So
  hnlich wie z.B. auch in C++ knnen nun Fehler ber sog. Ausnahmen
  mitgeteilt werden. Der Name kommt wohl daher, da diese Art der
  Fehlerbehandlung nicht im normalen Kontrollflu bercksichtigt wird
  (sowas wie: IF res < 0 THEN err...), sondern ein separater Prozedur- oder
  Modulblock dafr zustndig ist, der scheinbar asynchron zum normalen
  Programmablauf aktiviert wird. Neben den Ausnahmen, die vom Laufzeitsystem
  automatisch bei Fehlern erzeugt werden (Indextest usw.), knnen auch vom
  Programmierer mit der Funktion EXCEPTIONS.RAISE Ausnahmen erzeugt werden.
  Um auf die Ausnahmen (also auch Laufzeitfehler!) reagieren zu knnen, sind
  Ausnahmehandler erforderlich. Handler bestehen aus (fast) normalen Modul-
  oder Prozedurblcken, die mit dem neuen Schlsselwort EXCEPT eingeleitet
  werden. Jede Prozedur, jede Modulinitialisierung und auch jede
  Modulfinalisierung kann parallel einen Handler besitzen, der auf gleicher
  ``Ebene'' mit dem durch BEGIN oder FINALLY eingeleiteten normalen Block ist.
  Da es unterschiedliche Ausnahmen gibt, knnen auch die Handler selektiv
  bestimmten Ausnahmen zugeordnet werden.

  Wenn whrend der Ausfhrung des normalen Blocks irgendeine Ausnahme
  auftritt, wird folgendermaen vorgegangen:

  1) Wenn fr den Block ein Handler existiert und dieser auch fr die
     Ausnahme zustndig ist, wird der Handler ausgefhrt.

  2) Sonst wird in der Aufrufhierarchie soweit nach oben gegangen, bis
     entweder ein fr diese Ausnahme zustndiger Handler gefunden wird,
     der dann auch ausgefhrt wird, oder die oberste Ebene erreicht ist.
     Wenn kein Handler gefunden wurde, wird das Programm, evtl. mit
     einer Meldung, beendet.

  D.h. auch, da ein Programm bei einer Ausnahme beendet wird, wenn dieses
  ohne Bercksichtigung von Ausnahmen geschrieben wurde, was fr so ziemlich
  alle lteren Quelltexte gelten drfte.

  Damit ein Handler testen kann, ob er fr eine Ausnahme zustndig ist,
  mu jedes Bibliotheksmodul, das Ausnahmen auslsen kann, folgende Dinge
  exportieren:

    PROCEDURE Is<Name>Exception ( ): BOOLEAN;

  Diese Funktion ist immer notwendig. Fr Name wird ein passender Wert
  eingesetzt, z.B. der Modulname: IsIOException. Hiermit wird getestet,
  ob die Ausnahme vom betreffenden Modul ausgelst wurde.


    TYPE <Name>Exceptions = <Enumerationstyp>;

    PROCEDURE <Name>Exception ( ): <Name>Exceptions;

  Dieser Typ und diese Prozedur sind dann notwendig, wenn das Modul mehr
  als eine Ausnahme auslsen kann und deswegen eine Unterscheidung ntig
  ist.

  Der Handler kann also jetzt z.B. mit: IF IsIOException() THEN, oder
  IF IOException() = notAvailable THEN testen, ob er fr diese Ausnahme
  zustndig ist.

  Fr Laufzeitfehler-Ausnahmen existiert das Modul M2EXCEPTIONS, das alle
  diese Dinge mit dem Namen M2 exportiert. Somit kann also auch auf
  Laufzeitfehler in einer definierten Weise reagiert werden.

  Innerhalb eines Handlers gibt es nun mehrere Mglichkeiten: Wenn der
  Handler ein RETURN ausfhrt, dann wird die Prozedur oder der Modulrumpf
  normal beendet als wre keine Ausnahme aufgetreten. Wenn der Handler
  das neue Schlsselwort RETRY ausfhrt, wird der zugehrige normale Block,
  whrend dessen Ausfhrung die Ausnahme aufgetreten ist, nochmal wiederholt.
  Wird der Handler durch seine letzte Anweisung beendet, ohne da eine der
  vorigen Aktionen ausgefhrt wurde, wird in der Aufrufhierarchie nach
  dem nchsten Handler gesucht, so als wre dieser Handler nicht zustndig
  fr die Ausnahme. Der Handler kann auch selbst wiederum eine Ausnahme
  auslsen, wobei dann ebenfalls in der Aufrufhierarchie nach dem nchsten
  Handler gesucht wird.

  Dies war nur eine sehr grobe Beschreibung von Ausnahmen; die Definition
  im Standard ist beraus kompliziert durch die vielen Randbedingungen
  und Spezialflle. Hier soll nun noch ein Beispiel folgen, das dem Standard
  entnommen wurde:


    DEFINITION MODULE LibModule;

     TYPE LibExceptions = (brokenRubberBand, outOfRubberBands)

     PROCEDURE Fly;
     PROCEDURE ReplaceRubberBand;
     PROCEDURE SupplyRubberBands;

     PROCEDURE IsLibException (): BOOLEAN;
     PROCEDURE LibException (): LibExceptions;
    END LibModule.

    DEFINITION MODULE GeneralUserExceptions;

     TYPE GeneralExceptions = (problem, disaster);

     PROCEDURE RaiseGeneralException (exc:GeneralExceptions;text:ARRAY OF CHAR);
     PROCEDURE IsGeneralException (): BOOLEAN;
     PROCEDURE GeneralException (): GeneralExceptions;
    END GeneralUserExceptions;

    IMPLEMENTATION MODULE GeneralUserExceptions;

     IMPORT EXCEPTIONS;

     VAR generalSource : EXCEPTIONS.ExceptionSource;

     PROCEDURE RaiseGeneralException (exc:GeneralExceptions;text:ARRAY OF CHAR);
     BEGIN
       EXCEPTIONS.RAISE(generalSource, ORD(exc), text);
     END RaiseGeneralException;

     PROCEDURE IsGeneralException (): BOOLEAN;
     BEGIN
       RETURN(EXCEPTIONS.IsCurrentSource(generalSource));
     END IsGeneralException;

     PROCEDURE GeneralException (): GeneralExceptions;
     BEGIN
       RETURN(VAL(GeneralExceptions,EXCEPTIONS.CurrentNumber(generalSource)));
     END GeneralException;

    BEGIN
      EXCEPTIONS.AllocateSource(generalSource);
    END GeneralUserExceptions.

    ...

    FROM LibModule IMPORT
      Fly, ReplaceRubberBand, SupplyRubberBands, IsLibException, LibException,
      LibExceptions;

    FROM GeneralUserExceptions IMPORT
      GeneralExceptions, RaiseGeneralException,

    ...

    PROCEDURE TryFlying;
    BEGIN
      Fly; (* Kann Ausnahme vom Typ 'LibException' ausloesen *)
    EXCEPT
      (* Ausnahme-Handler *)
      IF IsLibException() THEN
        (* Ausnahme vom Modul 'LibModule' ausgeloest *)
        CASE LibException() OF
         |brokenRubberBand:
            ReplaceRubberBand;
            RETRY;
            (* Nicht so schlimm, nochmal "Fly" probieren *)
        ELSE
        END;
      END;
      (* Ausnahme kann nicht von diesem Handler behandelt werden. Da kein
       * RETURN, wird die Ausnahme an die aufrufende Prozedur
       * (-> "KeepFlying") weitergereicht.
       *)
    END TryFlying;

    PROCEDURE KeepFlying;
    BEGIN
      TryFlying;
    EXCEPT
      (* Ausnahme-Handler *)
      IF IsLibException() THEN
        (* Ausnahme vom Modul 'LibModule' ausgeloest *)
        CASE LibException() OF
         |outOfRubberBands:
            SupplyRubberBands;
            RETRY;
            (* Nicht so schlimm, nochmal "TryFlying" probieren *)
        ELSE
          (* Keine weitere Bearbeitung moeglich, also abbrechen und
           * normal zur aufrufenden Prozedur zurueckkehren.
           *)
          RETURN;
        END;
      ELSE
        (* Was ist nun los? *)
        RaiseGeneralException(disaster, "Unexpected exception from LibModule");
      END;
    END KeepFlying;


Literatur:
==========
Siehe ISO_LIB.TXT
